﻿/// 显示三角形和一个4边形
/// 组建vtkPolyData-四面体/五面体/六面体
///
#include <vtkSmartPointer.h>
#include <vtkPolygon.h>
#include <vtkTriangle.h>
#include <vtkCellArray.h>
#include <vtkPointData.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkProperty.h>
#include "vtkTriangleFilter.h"

#include <iostream>
#include <vtkFloatArray.h>

class DisplayUtil {
public:
    enum DisplayType {
        DisplayType_FRAME = 0,
        DisplayType_POINT,
        DisplayType_SURFACE,
    };

    static void displayVtkData(const char * title, vtkPolyData * data, const DisplayType type=DisplayType_FRAME)
    {
        vtkSmartPointer<vtkPolyDataMapper> mapper =
            vtkSmartPointer<vtkPolyDataMapper>::New();
        // 两种方法都可以
        mapper->SetInputData(data);
        //mapper->SetScalarRange(0, 7);

        vtkSmartPointer<vtkActor> actor =
            vtkSmartPointer<vtkActor>::New();
        actor->SetMapper(mapper);
        switch (type) {
        case DisplayType_FRAME:
            actor->GetProperty()->SetRepresentationToWireframe();
            break;
        case DisplayType_POINT:
            actor->GetProperty()->SetRepresentationToPoints();
            break;
        default:
            actor->GetProperty()->SetRepresentationToSurface();
            break;
        }


        vtkSmartPointer<vtkRenderer> renderer =
            vtkSmartPointer<vtkRenderer>::New();
        renderer->AddActor(actor);
        renderer->SetBackground(0.5, 0.5, 0.5);

        vtkSmartPointer<vtkRenderWindow> renderWindow =
            vtkSmartPointer<vtkRenderWindow>::New();
        renderWindow->AddRenderer(renderer);
        renderWindow->SetSize( 640, 480 );
        renderWindow->Render();
        renderWindow->SetWindowName(title);


        vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
            vtkSmartPointer<vtkRenderWindowInteractor>::New();
        renderWindowInteractor->SetRenderWindow(renderWindow);

        renderWindow->Render();
        renderWindowInteractor->Start();
    }
};

/// 顶点、多顶点、线、多线段、三角形等
class Graph2D {
public:
    static void doTriangle() {
        vtkSmartPointer<vtkPoints> points =
            vtkSmartPointer<vtkPoints>::New();
        points->InsertNextPoint(0.0, 0.0, 0.0);
        points->InsertNextPoint(1.0, 0.0, 0.0);
        points->InsertNextPoint(1.0, 1.0, 0.0);
        points->InsertNextPoint(0.0, 1.0, 0.0);
        points->InsertNextPoint(2.0, 0.0, 0.0);

        vtkSmartPointer<vtkPolygon> polygon =
            vtkSmartPointer<vtkPolygon>::New();
        polygon->GetPointIds()->SetNumberOfIds(4);
        polygon->GetPointIds()->SetId(0, 0);
        polygon->GetPointIds()->SetId(1, 1);
        polygon->GetPointIds()->SetId(2, 2);
        polygon->GetPointIds()->SetId(3, 3);

        vtkSmartPointer<vtkTriangle> trianle =
            vtkSmartPointer<vtkTriangle>::New();
        trianle->GetPointIds()->SetId(0, 1);
        trianle->GetPointIds()->SetId(1, 2);
        trianle->GetPointIds()->SetId(2, 4);

        vtkSmartPointer<vtkCellArray> cells =
            vtkSmartPointer<vtkCellArray>::New();
        cells->InsertNextCell(polygon);
        cells->InsertNextCell(trianle);

        vtkSmartPointer<vtkPolyData> polygonPolyData =
            vtkSmartPointer<vtkPolyData>::New();
        polygonPolyData->SetPoints(points);
        polygonPolyData->SetPolys(cells);

        vtkSmartPointer<vtkTriangleFilter> filter = vtkSmartPointer<vtkTriangleFilter>::New();
        filter->SetInputData(polygonPolyData);
        filter->Update();

        // 两种方法都可以
        DisplayUtil::displayVtkData("vtkTriangleFilter", polygonPolyData);
        //DisplayUtil::displayVtkData("vtkTriangleFilter", filter->GetOutput());

    }
};

/// 基础3D图像， 四面体/五面体/六面体
class Graph3D {

public:
    /// 显示立方体
    static void doCube() {
        static float x[8][3] = {
            {0,0,0},{1,0,0},{1,1,0},{0,1,0},
            {0,0,1},{1,0,1},{1,1,1},{0,1,1}
        };

        static vtkIdType pts[6][4] = {
            {0,1,2,3},{4,5,6,7},{0,1,5,4},
            {1,2,6,5},{2,3,7,6},{3,0,4,7}
        };

        vtkSmartPointer<vtkPolyData> cube = vtkSmartPointer<vtkPolyData>::New();
        vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
        vtkSmartPointer<vtkCellArray> polys = vtkSmartPointer<vtkCellArray>::New();
        vtkSmartPointer<vtkFloatArray> scalars = vtkSmartPointer<vtkFloatArray>::New();

        for (int i = 0; i < 8; i++) points->InsertPoint(i, x[i]);  // 8个点
        for (int i = 0; i < 6; i++) polys->InsertNextCell(4, pts[i]); // 6个面
        for (int i = 0; i < 8; i++) scalars->InsertTuple1(i, 0.5);

        cube->SetPoints(points);
        cube->SetPolys(polys);
        cube->GetPointData()->SetScalars(scalars);

        DisplayUtil::displayVtkData("cube", cube, DisplayUtil::DisplayType_SURFACE);
    }

    /// 显示多边形的边框线
    static void doCubeFrame() {
        static float x[8][3] = {
            {0,0,0},{1,0,0},{1,1,0},{0,1,0},
            {0,0,1},{1,0,1},{1,1,1},{0,1,1}
        };

        vtkSmartPointer<vtkPolyData> cube = vtkSmartPointer<vtkPolyData>::New();
        vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
        vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New();

        for (int i = 0; i < 8; i++) points->InsertPoint(i, x[i]);
        lines->InsertNextCell(5);
        for (unsigned long i = 0; i < 4; ++i) {
            lines->InsertCellPoint(i);
        }
        lines->InsertCellPoint(0);
        lines->InsertNextCell(5);
        for (unsigned long i = 0; i < 4; ++i) {
            lines->InsertCellPoint(i + 4);
        }
        lines->InsertCellPoint(4);

        for (int i = 0; i < 4; i++) {
            lines->InsertNextCell(2);
            lines->InsertCellPoint(i);
            lines->InsertCellPoint(i + 4);
        }

        cube->SetPoints(points);
        cube->SetLines(lines);

        DisplayUtil::displayVtkData("cubeFrame", cube, DisplayUtil::DisplayType_FRAME);
    }

    /// 四面体
    static void doTetrahedron() {
        const int num_points = 4;
        static float x[num_points][3] = {
            {0,0,0},{1,0,0},{0.5,1,0},{0.5,0.5,1}
        };

        static vtkIdType pts[4][3] = {
            {0,1,2},{0,1,3},{1,2,3},{0,2,3}
        };
        vtkSmartPointer<vtkPolyData> cube = vtkSmartPointer<vtkPolyData>::New();
        vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
        vtkSmartPointer<vtkCellArray> polys = vtkSmartPointer<vtkCellArray>::New();
        vtkSmartPointer<vtkFloatArray> scalars = vtkSmartPointer<vtkFloatArray>::New();

        for (int i = 0; i < num_points; i++) points->InsertPoint(i, x[i]);
        for (int i = 0; i < 4; i++) polys->InsertNextCell(3, pts[i]);
        for (int i = 0; i < num_points; i++) scalars->InsertTuple1(i, i);

        cube->SetPoints(points);
        cube->SetPolys(polys);
        cube->GetPointData()->SetScalars(scalars);

        DisplayUtil::displayVtkData("tetrahedron", cube, DisplayUtil::DisplayType_SURFACE);
    }

    static void doTetrahedronFrame() {
        const int num_points = 4;
        static float x[num_points][3] = {
            {0,0,0},{1,0,0},{0.5,1,0},{0.5,0.5,1}
        };

        static vtkIdType pts[4][3] = {
            {0,1,2},{0,1,3},{1,2,3},{0,2,3}
        };
        vtkSmartPointer<vtkPolyData> cube = vtkSmartPointer<vtkPolyData>::New();
        vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
        vtkSmartPointer<vtkCellArray> lines = vtkSmartPointer<vtkCellArray>::New();


        for (int i = 0; i < num_points; i++) points->InsertPoint(i, x[i]);
        for (int i = 0; i < num_points - 1; i++) {
            lines->InsertNextCell(2);
            lines->InsertCellPoint(i);
            lines->InsertCellPoint(num_points - 1);

            lines->InsertNextCell(2);
            lines->InsertCellPoint(i);
            lines->InsertCellPoint((i + 1) % (num_points - 1));
        }

        cube->SetPoints(points);
        cube->SetLines(lines);


        DisplayUtil::displayVtkData("tetrahedron", cube, DisplayUtil::DisplayType_SURFACE);
    }
};


enum OpEn {
    Op_doTriangle =1,
    Op_doCube,
    Op_doCubeFrame,
    Op_doTetrahedron,
    Op_doTetrahedronFrame,
};

///
/// \brief 演示如何建立一个图形，如何使用vtkTriangleFilter 把输入数据全部转成三角形
/// \param argc
/// \param argv
/// \return
///
int main(int argc, char *argv[])
{
    int option = Op_doTriangle;
    if (argc == 2) {
        option = atoi(argv[1]);
    }

    switch (option) {
    case Op_doTriangle:
        break;
    case Op_doCube:
        Graph3D::doCube();
        break;
    case Op_doCubeFrame:
        Graph3D::doCubeFrame();
        break;
    case Op_doTetrahedron:
        Graph3D::doTetrahedron();
        break;
    case Op_doTetrahedronFrame:
        Graph3D::doTetrahedronFrame();
        break;
    default:
        break;
    }

    return EXIT_SUCCESS;
}
